package com.gitee.feizns.quickstart.sa.token;

import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.StpLogic;
import cn.dev33.satoken.stp.StpUtil;
import com.gitee.feizns.dynamic.Ex;

import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 会话工具类
 * <p>
 *     对常用的用户session方法进行封装
 * </p>
 * @author feizns
 * @since 2023/3/8
 */
public class UserStpLogic<ID, T extends User<ID>> {

    /**
     * 存储当前登录用户信息的session key
     */
    public static final String CURRENT_USER_SESSION_KEY = "CURRENT_USER_SESSION_KEY";

    /**
     * {@link StpLogic}
     */
    private final StpLogic stpLogic;

    /**
     * 用户类型
     */
    private final Class<T> userType;

    /**
     * 指定用户类型
     * @param userType 用户类型
     */
    public UserStpLogic(Class<T> userType) {
        this(StpUtil.getStpLogic(), userType);
    }

    /**
     * 指定用户类型和{@link StpLogic}
     * @param stpLogic {@link StpLogic}
     * @param userType 用户类型
     */
    public UserStpLogic(StpLogic stpLogic, Class<T> userType) {
        this.stpLogic = stpLogic;
        this.userType = userType;
    }

    /**
     * 获取当前登录的用户
     * @return token
     */
    public T current() {
        stpLogic.checkLogin();
        return current(stpLogic.getSession());
    }

    /**
     * 获取当前登录的用户 未登录返回null
     * @return token
     */
    public T currentDefaultNull() {
        return current(getSessionOrNull());
    }

    /**
     * 获取当前登录的用户（不存在的返回默认值）
     * @return token
     */
    public T current(T defaultVal) {
        return Optional.ofNullable(current(getSessionOrNull()))
                .orElse(defaultVal);
    }

    /**
     * 根据会话获取当前用户
     * @return token
     */
    public T current(SaSession saSession) {
        return saSession != null ? saSession.getModel(CURRENT_USER_SESSION_KEY, userType) : null;
    }

    /**
     * 获取指定标识的用户
     * @return token
     */
    public T current(Long id) {
        SaSession session = stpLogic.getSessionByLoginId(id);
        return session != null ? session.getModel(CURRENT_USER_SESSION_KEY, userType) : null;
    }

    /**
     * 获取当前用户的某个属性
     * @return token
     */
    public <U> U current(Function<T, U> mapper) {
        return Optional.ofNullable(current()).map(mapper).orElse(null);
    }

    /**
     * 获取所有的在线的用户
     * @return token
     */
    public List<T> onlineUsers() {
        return sessionIds().stream()
                .map(StpUtil::getSessionBySessionId)
                .filter(Objects::nonNull)
                .map(this::current)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }

    /**
     * 将账号登录并将当前用户信息存储到会话中
     * @param user 用户
     * @return token
     */
    public String login(T user) {
        stpLogic.login(user.getId());
        stpLogic.getSession().set(CURRENT_USER_SESSION_KEY, user);
        return stpLogic.getTokenValue();
    }

    /**
     * 将当前账号注销
     */
    public void logout() {
        stpLogic.checkLogin();
        stpLogic.logout();
    }

    /**
     * 将指定用户账号注销
     */
    public void logout(ID id) {
        stpLogic.checkLogin();
        stpLogic.logout(id);
    }

    /**
     * 获得会话或null
     * @return {@link SaSession}
     */
    public SaSession getSessionOrNull() {
        return Ex.tryCatch(() -> stpLogic.getSession())
                .ignoreException(NotLoginException.class)
                .orElse(null);
    }

    /**
     * 获取所有正在登录的sessionId列表
     * @return sessionId
     */
    public List<String> sessionIds() {
        return stpLogic.searchSessionId("", 0, -1, true);
    }

}
